<?php

namespace app\service;

use OSS\Core\OssException;
use OSS\OssClient;
use think\Db;
use think\Cache;

class RechargeWithdrawMatcher {
    public static function matchWithdrawOrdersFIFO($recharge_order_no, $rechargeAmount, $rechargeType = 0)
    {
        $rate = 0.993;
        $remainingAmount = floor($rechargeAmount * $rate * 100) / 100;
        $shoperAmount = $rechargeAmount - $remainingAmount;
        $matchedWithdraws = [];
        $redis = Cache::store('redisQueue')->handler();
        $redisQueue = new RedisQueue();
        
        // 临时存储需要重新入队的订单
        $tempQueue = [];
        
        // 循环处理直到金额用完或队列为空
        while ($remainingAmount > 0) {
            // 从队列头部取出一个元素(真正的FIFO)
            $item = $redisQueue->pop('withdraw');
            
            if (!$item) {
                // 队列已空，跳出循环
                break;
            }
            
            $withdrawNo = $item['withdraw_no'];

            // 获取分布式锁
            $lockKey = "lock:withdraw:{$withdrawNo}";
            $lockId = self::acquireLock($redis, $lockKey, 5); // 5秒超时
            
            if (!$lockId) {
                // 获取锁失败，暂存到临时队列后面再处理
                $tempQueue[] = $item;
                continue;
            }
            
            try {
                Db::startTrans();
                
                // 获取提现订单详情(加行锁)
                $order = Db::name('withdraw')
                    ->where('order_no', $withdrawNo)
                    ->where('status', '<', 2)
                    ->where('expire_time', '>', time())
                    ->lock(true)
                    ->find();

                if (!$order) {
                    Db::commit();
                    continue; // 订单无效，跳过
                }
                
                $neededAmount = $order['money'] - $order['paid_amount'];
                if ($neededAmount <= 0) {
                    Db::commit();
                    continue; // 订单已全额匹配，跳过
                }
                
                $matchAmount = min($neededAmount, $remainingAmount);
                
                // 创建关联记录
                $relId = Db::name('withdraw_recharge_mapping')->insertGetId([
                    'withdraw_no' => $order['order_no'],
                    'recharge_order_no' => $recharge_order_no,
                    'mapping_amount' => $matchAmount,
                    'recharge_type' => $rechargeType,
                    'create_time' => time()
                ]);
                
                if (!$relId) {
                    Db::rollback();
                    $tempQueue[] = $item; // 失败，重新入队
                    continue;
                }
                
                // 更新提现订单状态
                $newPaidAmount = $order['paid_amount'] + $matchAmount;
                $status = ($newPaidAmount >= $order['money']) ? 2 : 1;
                
                $update = Db::name('withdraw')
                    ->where('order_no', $order['order_no'])
                    ->update([
                        'paid_amount' => $newPaidAmount,
                        'status' => $status
                    ]);

                if (!$update) {
                    Db::rollback();
                    $tempQueue[] = $item; // 失败，重新入队
                    continue;
                }
                
                $remainingAmount -= $matchAmount;
                $matchedWithdraws[] = [
                    'user_id' => $order['userid'],
                    'withdraw_no' => $order['order_no'],
                    'matched_amount' => $matchAmount
                ];
                
                Db::commit();
                
                // 如果提现订单未完成，重新放回队列头部(确保优先处理)
                if ($status != 2) {
                    $redisQueue->push('withdraw', [
                        'withdraw_no' => $order['order_no'],
                        'create_time' => $item['create_time'] // 保持原创建时间
                    ]);
                }
                
            } catch (\Exception $e) {
                Db::rollback();
                $tempQueue[] = $item; // 异常，重新入队
                // 记录日志...
            } finally {
                self::releaseLock($redis, $lockKey, $lockId);
            }
        }
        
        // 将未能处理的订单重新放回队列头部(保持原有顺序)
        foreach (array_reverse($tempQueue) as $item) {
            $redisQueue->push('withdraw', $item);
        }
        
        return [
            'code' => 200,
            'msg' => '匹配成功',
            'data' => [
                'matched_withdraws' => $matchedWithdraws,
                'remaining_amount' => $remainingAmount,
                'shoper_amount' => $shoperAmount
            ]
        ];
    }


    // 获取分布式锁
    public static function acquireLock($redis, $key, $timeout)
    {
        $identifier = uniqid();
        $end = time() + $timeout;
        
        while (time() < $end) {
            if ($redis->setnx($key, $identifier)) {
                $redis->expire($key, $timeout);
                return $identifier;
            }
            
            usleep(100000); // 等待100ms
        }
        
        return false;
    }
    // 释放分布式锁
    public static function releaseLock($redis, $key, $identifier)
    {
        $script = '
            if redis.call("get", KEYS[1]) == ARGV[1] then
                return redis.call("del", KEYS[1])
            else
                return 0
            end
        ';
        
        $redis->eval($script, [$key, $identifier], 1);
    }


    public static function removeFromWithdrawQueue($withdrawNo)
    {
        // 同Topup控制器中的方法
        $redisQueue = new RedisQueue();
        $queueItems = $redisQueue->all('withdraw');
        
        $redis = Cache::store('redisQueue')->handler();
        $redis->del('withdraw');
        
        foreach ($queueItems as $item) {
            if ($item['withdraw_no'] != $withdrawNo) {
                $redisQueue->push('withdraw', $item);
            }
        }
    }
}